﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using SuperMap.Desktop;
using SuperMap.Desktop.UI;
using SuperMap.Data;
using SuperMap.UI;
using SuperMap.Analyst;
using SuperMap.Analyst.SpatialAnalyst;
using SuperMap.Analyst.NetworkAnalyst;
using System.Diagnostics;

namespace DesktopPlugin1
{

    //网络核密度分析算法
    class NetDenAnalysist
    {
        #region 定义变量

        //从外界获得变量
        private DatasetVector inPointDataset;//输入点数据集
        private Dataset inLineDataset;//输入线数据集
        private String datasetGridName;//栅格数据集的名称
        private double tolerance;//道路容差
        private Datasource outDataSource;//输出的数据源
        private double resolution;//输出栅格大小
        private double searchRound;//传入搜索半径
        //获得输入点数据集(中心点)的坐标
        private Dictionary<double, Point2D> inPoints = new Dictionary<double, Point2D>();//用于保存输入的点数据集的坐标
        //寻找相应距离的点
        private Dictionary<double, Point2D> points = new Dictionary<double, Point2D>();//用于保存每一条线上新添加的点
        //创建网络点数据集并添加数据
        private DatasetVector pointAddDataset;//根据线上的点创建的点数据集
        private Recordset recordPointSet;//创建点数据记录集
        //构建网络数据集
        private DatasetVector datasetNet;//根据线数据生成网络数据集
        private DatasetVector datasetNetNode;//网络数据集的结点数据集
        private string[] linefieldNames = null;
        //寻找点数据集中每个点最近的结点
        private Recordset recordNodeSet;//创建结点数据记录集
        private Recordset recordNet;//创建弧段数据记录集
        private DatasetVector minDisPointsSet;//距离最近点数据集
        private Dictionary<int, int> startPoints = new Dictionary<int, int>();//生成发生元
        //将弧段和结点一一对应
        Dictionary<int, EdgeProperty> edgeToNode = new Dictionary<int, EdgeProperty>();//创建弧段属性集
        //构建交通网络分析模型
        TransportationAnalyst transportationAnalyst = new TransportationAnalyst();//创建交通网络分析对象
        TransportationAnalystParameter parameter = new TransportationAnalystParameter();//构建交通网络分析参数对象并设置其属性
        //进行服务区分析
        private Dictionary<int, int> nodeCount = new Dictionary<int, int>();//通过记录结点的ID记录该结点上发生元多少
        private Dictionary<int, int> startNode = new Dictionary<int, int>();//生成发生元结点
        private ServiceAreaResult serviceAreaResult;//服务区分析结果
        private int[][] allEdgesID;//获得所有经过弧段的ID
        //将密度值添加进数据集

        #endregion

        #region 从外界获得变量

        public void getInformation(string name, double tole, Dataset pDataset,
            Dataset lDataset, Datasource datasource, double radio, double px)
        {
            datasetGridName = name;//传入栅格数据集的名称
            tolerance = tole;//传入道路容差
            resolution = px;//传入网络栅格大小
            inPointDataset = (DatasetVector)pDataset;//传入分析点数据集
            inLineDataset = lDataset;//传入分析线数据集
            outDataSource = datasource;//传入输出的数据源
            searchRound = radio;//传入搜索半径
        }

        #endregion

        #region 获得输入点数据集(中心点)的坐标

        public void getInPoints()
        {
            double count = 0;//记录点总个数
            string smid = "SmID<={0}";
            Point2D[] inputPoint = new Point2D[inPointDataset.RecordCount];//新建点
            // 依据SmID的顺序得到数据集中所有的记录
            Recordset recordset = inPointDataset.Query(string.Format(smid, inPointDataset.RecordCount), CursorType.Static);
            recordset.MoveFirst();//从第一条记录开始

            for (int i = 0; i < inPointDataset.RecordCount; i++)
            {
                inputPoint[i].X = (double)recordset.GetFieldValue(1);//获取点的X坐标
                inputPoint[i].Y = (double)recordset.GetFieldValue(2);//获取点的Y坐标
                inPoints.Add(count++, inputPoint[i]);//将点添加进数组中
                recordset.MoveNext();//移动到下一条记录
            }
            SuperMap.Desktop.Application.ActiveApplication.Output.Output("获取点集成功！");
        }

        #endregion

        #region 寻找相应距离的点

        public void FindDisPoint(DatasetVector lineVector)
        {
            double count = 0;//记录点总个数
            double length;//线的长度
            Feature feature;//特征要素
            Geometry geometry;//几何对象
            GeoLine geoLine;//线要素
            double distance = resolution;//获取初始距离
            int vectorCount = lineVector.RecordCount;//线数据集中记录的个数
            double percent = 0;

            //依据SmID的顺序得到数据集中所有的记录
            Recordset recordset = lineVector.GetRecordset(false, CursorType.Static);
            recordset.MoveFirst();

            for (int i = 0; i < vectorCount; i++)
            {
                distance = resolution;//设置初始距离
                length = (double)recordset.GetFieldValue("SmLength");//获取线的长度
                feature = recordset.GetFeature();//获取特征要素
                geometry = feature.GetGeometry();// 获取特征要素对应的几何对象
                geoLine = (GeoLine)geometry;//将几何要素转换为线要素

                if (i == 0)
                {
                    percent = geoLine.Length / length;
                }

                while (distance < length)
                {
                    try
                    {
                        //将获取的记录输入points点数组中
                        points.Add(count++, geoLine.FindPointOnLineByDistance(distance * percent));
                    }
                    catch
                    {
                        MessageBox.Show("存在相同ID的点！");
                    }
                    distance += resolution;
                }

                recordset.MoveNext();//移动到下一条记录
            }
            SuperMap.Desktop.Application.ActiveApplication.Output.Output("寻找结点成功！");
        }

        #endregion

        #region 创建网络点数据集并添加数据

        public void CreateNetPointDataset()
        {
            //创建点数据集
            DatasetVectorInfo datasetCectorInfo = new DatasetVectorInfo();
            String datasetName = "网络栅格化节点";
            //如果数据集中包含了该名字的数据集，则删除
            if (outDataSource.Datasets.Contains(datasetName))
            {
                outDataSource.Datasets.Delete(datasetName);
                //pointAddDataset = (DatasetVector)outDataSource.Datasets[outDataSource.Datasets.IndexOf("网络栅格化节点")];
            }

            // 设置矢量数据集的信息
            DatasetVectorInfo datasetVectorInfo = new DatasetVectorInfo();
            datasetVectorInfo.Type = DatasetType.Point;//设置为点数据集
            datasetVectorInfo.IsFileCache = true;
            datasetVectorInfo.Name = datasetName;

            pointAddDataset = outDataSource.Datasets.Create(datasetVectorInfo);//在数据集合下创建一个点数据集
            //获得数据集中的记录集
            pointAddDataset = outDataSource.Datasets[datasetName] as DatasetVector;
            recordPointSet = pointAddDataset.GetRecordset(false, CursorType.Dynamic);

            //设置批量更新
            recordPointSet.Batch.MaxRecordCount = points.Count;
            recordPointSet.Batch.Begin();
            //新建点几何对象
            Point2D pointNew = new Point2D();
            GeoPoint geoPoint = new GeoPoint(pointNew);

            //向记录集中添加点几何对象
            for (int i = 0; i < points.Count; i++)
            {
                geoPoint.X = points[i].X;
                geoPoint.Y = points[i].Y;
                //将点的几何信息添加到记录集中
                recordPointSet.AddNew(geoPoint);
            }
            recordPointSet.Update();

            //关闭记录集
            recordPointSet.Close();
            recordPointSet.Dispose();

            SuperMap.Desktop.Application.ActiveApplication.Output.Output("创建网络结点数据集成功！");
        }
        #endregion

        #region 构建网络数据集

        public DatasetVector BuildNetwork()
        {
            //检查是否存在与结果数据集同名的数据集，如存在则删除
            string targetDatasetName = datasetGridName;//网络栅格化数据集的名称
            if (outDataSource.Datasets.Contains(targetDatasetName))
            {
                outDataSource.Datasets.Delete(targetDatasetName);
            }

            //新建网络数据集，传入线数据集，输出数据源，并命名为网络栅格化数据集
            datasetNet = NetworkBuilder.BuildNetwork((DatasetVector)inLineDataset,
                linefieldNames, outDataSource, targetDatasetName);

            //更改网络数据集的坐标系
            if (datasetNet.PrjCoordSys != null)
            {
                pointAddDataset.PrjCoordSys = datasetNet.PrjCoordSys;
            }

            //将原有的网络数据集与点数据集叠加，形成新的网络栅格化数据集
            DatasetVector[] newDatasetNet = new DatasetVector[] { pointAddDataset };

            bool appendResult = false;//判断是否生成成功
            try
            {
                SuperMap.Desktop.Application.ActiveApplication.Output.Output("正在构建网络栅格化数据集，" +
            "时间较长，数据量较大时可能会持续几分钟，请耐心等待");
                appendResult = NetworkBuilder.AppendToNetwork((DatasetVector)datasetNet, newDatasetNet);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
            if (appendResult)//如果成功，输出
                SuperMap.Desktop.Application.ActiveApplication.Output.Output("网络栅格化数据集生成成功！");

            datasetNetNode = datasetNet.ChildDataset;//获取网络数据集的结点数据集
            return datasetNet;

        }

        #endregion

        #region 向网络数据集中添加字段
        public void addFieldInfo()
        {
            //添加核密度分析值字段

            // 实例化一个字段信息对象，对其进行设置
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.Name = "NetWorkDensityGridValue";
            fieldInfo.Caption = "网络核密度分析栅格值";
            fieldInfo.DefaultValue = "0";
            fieldInfo.Type = FieldType.Double;

            // 将字段添加到数据集中
            FieldInfos fieldInfos = datasetNet.FieldInfos;
            fieldInfos.Add(fieldInfo);
            fieldInfo.Dispose();

            //添加总路程字段
            /*
            // 实例化一个字段信息对象，对其进行设置
            FieldInfo fieldInfoName = new FieldInfo();
            fieldInfoName.Name = "NetWorkDensityDistance";
            fieldInfoName.Caption = "网络核密度分析总路程值";
            fieldInfoName.DefaultValue = "0";
            fieldInfoName.Type = FieldType.Double;

            // 将字段添加到数据集中
            FieldInfos fieldInfosName = datasetNet.FieldInfos;
            fieldInfosName.Add(fieldInfoName);
            fieldInfoName.Dispose();*/
        }
        #endregion

        #region 寻找点数据集中每个点最近的结点
        public void ComputeMinDistance()
        {
            string smid = "SmID<={0}";

            //创建结点数据记录集
            recordNodeSet = datasetNetNode.GetRecordset(false, CursorType.Dynamic);
            //创建弧段数据记录集
            recordNet = datasetNet.GetRecordset(false, CursorType.Dynamic);
            //生成点记录集
            Recordset inpointRecord = inPointDataset.GetRecordset(false, CursorType.Dynamic);

            //如果存在数据源中存在该记录集，则删除
            if (outDataSource.Datasets.Contains("距离最近点记录集"))
                outDataSource.Datasets.Delete("距离最近点记录集");

            SuperMap.Desktop.Application.ActiveApplication.Output.Output("正在寻找每一个中心点最近的结点，"+
            "时间较长，数据量较大时可能会持续几分钟，请耐心等待");

            #region 时间较长,约2分钟
            //寻找每个中心点最近的结点
            ProximityAnalyst.ComputeMinDistance(inpointRecord, recordNodeSet, 0, tolerance, outDataSource, "距离最近点记录集");
            SuperMap.Desktop.Application.ActiveApplication.Output.Output("已找到所有中心点最近结点！");
            #endregion

            //获取距离最近点记录集
            minDisPointsSet = (DatasetVector)outDataSource.Datasets[outDataSource.Datasets.IndexOf("距离最近点记录集")];
            Recordset minDisPointsRecordset = minDisPointsSet.Query(string.Format(smid, inpointRecord.RecordCount), CursorType.Static); ;
            minDisPointsRecordset.MoveFirst();

            //将中心点最近结点(发生元)的ID添加到数组中
            for (int i = 0; i < inpointRecord.RecordCount; i++)
            {
                startPoints.Add(i, Convert.ToInt32(minDisPointsRecordset.GetFieldValue(3)));
                minDisPointsRecordset.MoveNext();
            }
        }

        #endregion

        #region 将弧段和结点一一对应

        public void EdgeToNode()
        {
            recordNet.MoveFirst();
            for (int i = 0; i < recordNet.RecordCount; i++)
            {
                EdgeProperty edge = new EdgeProperty();

                edge.EdgeID = Convert.ToInt32(recordNet.GetFieldValue("SmID"));
                edge.FNodeID = Convert.ToInt32(recordNet.GetFieldValue("SmFNode"));
                edge.TNodeID = Convert.ToInt32(recordNet.GetFieldValue("SmTNode"));
                edge.Length = Convert.ToInt32(recordNet.GetFieldValue("SmLength"));

                edgeToNode.Add(Convert.ToInt32(recordNet.GetFieldValue("SmID")), edge);
                recordNet.MoveNext();
            }

        }

        #endregion

        #region 构建交通网络分析模型

        public void TransportationAnalystSetting()
        {
            //构建一个网络分析环境设置实例并设置其属性
            TransportationAnalystSetting transportationAnalystSetting = new TransportationAnalystSetting();
            transportationAnalystSetting.NetworkDataset = datasetNet;
            transportationAnalystSetting.EdgeIDField = "SmID";
            transportationAnalystSetting.NodeIDField = "SmNodeID";
            transportationAnalystSetting.FNodeIDField = "SmFNode";
            transportationAnalystSetting.TNodeIDField = "SmTNode";
            //网络分析权值字段设置
            WeightFieldInfo weightInfo = new WeightFieldInfo();
            weightInfo.FTWeightField = "smLength";
            weightInfo.TFWeightField = "smLength";
            transportationAnalystSetting.WeightFieldInfos.Add(weightInfo);
            //设置交通网络分析对象属性
            transportationAnalyst.AnalystSetting = transportationAnalystSetting;
            //加载网络模型
            bool isLoad = transportationAnalyst.Load();
            if (isLoad)
                SuperMap.Desktop.Application.ActiveApplication.Output.Output("加载交通网络模型成功！");

        }

        #endregion

        #region 进行服务区分析

        public void ServiceAnalyst()
        {

            parameter.IsEdgesReturn = true;//设置分析结果中包含途经弧段集合
            parameter.IsNodesReturn = true;//设置分析结果中包含分析途经的结点集合
            parameter.IsRoutesReturn = true;//设置返回路由结果

            //新建结点数组，并记录每个结点上的发生元数目
            int nodeID;
            for (int i = 0; i <= datasetNetNode.RecordCount; i++)
            {
                nodeCount[i] = 0;//初始化结点数组
            }

            //遍历每一个发生元，将其计算入相应的ID中
            for (int i = 0; i < startPoints.Count; i++)
            {
                nodeID = startPoints[i];
                nodeCount[nodeID]++;
            }

            //选出是发生元的结点
            int count = 0;
            for (int i = 0; i < nodeCount.Count; i++)
            {
                if (nodeCount[i] != 0)
                    startNode[count++] = i;
            }

            SuperMap.Desktop.Application.ActiveApplication.Output.Output("正在寻找每一个中心点阈值内的弧段，" +
"时间较长，数据量较大时可能会持续几分钟，请耐心等待");
            //进行服务区分析
            for (int i = 0; i < startNode.Count; i++)
            {
                //设置发生元结点ID
                parameter.Nodes = new Int32[] { startNode[i] };
                //设置服务区半径数组
                double[] searchRoundArray = new double[1];
                searchRoundArray[0] = searchRound;
                try
                {
                    //进行网络分析，查找服务区
                    serviceAreaResult = transportationAnalyst.FindServiceArea(parameter, searchRoundArray, true, false);
                }
                catch { }
                try
                {
                    //获取各条路径的途径弧段、结点
                    allEdgesID = serviceAreaResult.Edges;

                    for (int m = 0; m < allEdgesID[0].Length; m++)
                    {
                        //寻找最短路径计算距离
                        TransportationAnalystParameter findPathParameter = new TransportationAnalystParameter();
                        findPathParameter.IsRoutesReturn = true;

                        //设置两点分别为发生元结点与弧段相对应的结点
                        //分别求发生元与弧段两个结点的距离并进行比较
                        findPathParameter.Nodes = new int[] { startNode[i], edgeToNode[allEdgesID[0][m]].FNodeID };
                        TransportationAnalystResult findPathResult1 = transportationAnalyst.FindPath(findPathParameter, true);
                        findPathParameter.Nodes = new int[] { startNode[i], edgeToNode[allEdgesID[0][m]].TNodeID };
                        TransportationAnalystResult findPathResult2 = transportationAnalyst.FindPath(findPathParameter, true);

                        double distance = 0;//发生元与弧段的距离
                        //如果发生元在弧段上，设置为弧段另一个结点距离的二分之一
                        if (findPathResult1.Routes.Length == 0)
                        {
                            distance = findPathResult2.Routes[0].Length / 2;
                        }
                        else if (findPathResult2.Routes.Length == 0)
                        {
                            distance = findPathResult1.Routes[0].Length / 2;
                        }
                        else//如果发生元不在弧段上，设置为较长的弧段
                        {
                            //找到最短路径
                            distance = findPathResult1.Routes[0].Length > findPathResult2.Routes[0].Length ?
                                findPathResult1.Routes[0].Length : findPathResult2.Routes[0].Length;
                        }

                        //如果距离超过限度，则设为0
                        if (distance > searchRound)
                        {
                            distance = searchRound;
                        }

                        //网络核密度算法计算公式
                        double netDenValue = 0;
                        //获取该发生元附近的输入点集数目
                        edgeToNode[allEdgesID[0][m]].PointsNum = nodeCount[startNode[i]];
                        //进行计算
                        netDenValue = 3 * (searchRound * searchRound - distance * distance)
                            / (4 * searchRound * searchRound * searchRound * searchRound)
                            * edgeToNode[allEdgesID[0][m]].PointsNum;
                        netDenValue *= 100000;
                        edgeToNode[allEdgesID[0][m]].DensityValue += netDenValue;//计算总值
                    }
                }
                catch { }
            }
            SuperMap.Desktop.Application.ActiveApplication.Output.Output("阈值内弧段分析完毕！");
        }

        #endregion

        #region 将密度值添加进数据集

        public void addDensityValueToDataset()
        {
            recordNet.Batch.MaxRecordCount = edgeToNode.Count;
            recordNet.Batch.Begin();
            int ID = 0;

            SuperMap.Desktop.Application.ActiveApplication.Output.Output("正在计算网络核密度值，" +
"时间较长，数据量较大时可能会持续几分钟，请耐心等待");
            recordNet.MoveFirst();
            for (int i = 0; i < edgeToNode.Count; i++)
            {
                recordNet.Edit();//进行编辑

                ID = Convert.ToInt32(recordNet.GetFieldValue("SmID"));
                recordNet.SetDouble("NetWorkDensityGridValue", edgeToNode[ID].DensityValue);//设置总值
                //更新记录集

                recordNet.MoveNext();
            }
            recordNet.Update();
            recordNet.Dispose();//释放资源
        }

        #endregion

    }

}
